﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core.Framework.Model.Common;
using Core.Framework.Model.ViewModel;
using Core.Framework.Model.WebSockets;
using Core.Framework.Redis;
using Core.Framework.Redis.Queue_Helper;
using Core.Framework.Util;
using Core.IBusiness.ILoggerModule;
using Core.IBusiness.ISocketModule;
using Core.Middleware.WebSockets;

namespace Core.Business.SocketModule
{

    public class Queue : IQueue
    {

        /// <summary>
        /// 日志接口
        /// </summary>
        private ILog iLog;

        public Queue(ILog log)
        {
            this.iLog = log;
        }


        /// <summary>
        /// 发布消息
        /// </summary>
        /// <param name="model">接收参数</param>
        /// <returns></returns>
        public MethodResult<bool> Publish(QueryMessage model)
        {

            try
            {
                // 构造发送消息
                MessageQueue message = StructureMessage(model);

                if (message == null)
                    return new MethodResult<bool>{IsFinished = true, Date = false,Discription = "发布消息不可为NULL"};

                // 日志实体
                RunningLogerInfoModel logerInfo = new RunningLogerInfoModel { TaskMessage = model, Message = message };


                return new MethodResult<bool>
                {
                    IsFinished = true,
                    Date = this.TaskDistribution(model, logerInfo, message)
                };
            }
            catch (Exception e)
            {
                return new MethodResult<bool>{ IsFinished = false, Discription = e.Message};
            }


        }

        /// <summary>
        /// 测试发布消息
        /// </summary>
        /// <param name="model">接收参数</param>
        /// <returns></returns>
        public MethodResult<bool> DebugPublish(QueryMessageTest entity)
        {

            try
            {
                // 构造发送消息
                MessageQueue message = DebugStructureMessage(entity);

                if (message == null)
                    return new MethodResult<bool> { IsFinished = true, Date = false, Discription = "发布消息不可为NULL" };

                var model = entity.TryToJson().TryToEntity<QueryMessage>();

                // 日志实体
                RunningLogerInfoModel logerInfo = new RunningLogerInfoModel
                {
                    TaskMessage = model,
                    Message = message
                };


                return new MethodResult<bool>
                {
                    IsFinished = true,
                    Date = this.TaskDistribution(model, logerInfo, message)
                };
            }
            catch (Exception e)
            {
                return new MethodResult<bool> { IsFinished = false, Discription = e.Message };
            }


        }



        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="token">用户token</param>
        /// <param name="subChannel">订阅渠道</param>
        /// <returns></returns>
        public MethodResult<bool> Subscribe(string token, string subChannel)
        {

            try
            {
                int.TryParse(subChannel, out int kkkk);

                if (string.IsNullOrWhiteSpace(subChannel) || kkkk > 0)
                {
                    return new MethodResult<bool> { IsFinished = false, Discription = $"订阅渠道名不能为纯数字，不能为空。{subChannel}" };
                }

                // 构造链接数据
                ClientInfo client = 
                    ((string)RedisQueueHelper
                        .HashGet(RedisQueueHelperParameter.WebSocketByToken, token))
                    .TryToEntity<ClientInfo>();

                if(client == null)
                    return new MethodResult<bool> { IsFinished = false, Discription = "该用户尚未登录Ws" };

                List<string> list = client.User.Subscription.ToList();

                if (!list.Contains(subChannel))
                {
                    // list.Add(subChannel);

                    // client.User.Subscription = list.ToArray();

                    WebSocketApplication.ClientSubscribe(client, subChannel);

                    // 写入WS身份信息
                    RedisQueueHelper.HashSet(RedisQueueHelperParameter.WebSocketByToken, token, client.TryToJson());
                }

                return new MethodResult<bool>{ IsFinished = true, Date = true};
            }
            catch (Exception e)
            {
                return new MethodResult<bool>{ IsFinished = false, Discription = e.Message};
            }
            
        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <param name="token">用户token</param>
        /// <param name="subChannel">卸载订阅渠道</param>
        /// <returns></returns>
        public MethodResult<bool> UnSubscribe(string token, string subChannel)
        {
            try
            {
                int.TryParse(subChannel, out int kkkk);

                if (string.IsNullOrWhiteSpace(subChannel) || kkkk > 0)
                {
                    return new MethodResult<bool> { IsFinished = false, Discription = $"订阅渠道名不能为纯数字，不能为空。{subChannel}" };
                }

                // 构造链接数据
                ClientInfo client =
                    ((string)RedisQueueHelper
                        .HashGet(RedisQueueHelperParameter.WebSocketByToken, token))
                    .TryToEntity<ClientInfo>();

                List<string> list = client.User.Subscription.ToList();

                if (!list.Contains(subChannel))
                {
                    //list.Remove(subChannel);

                    //client.User.Subscription = list.ToArray();

                    WebSocketApplication.ClientUnSubscribe(client, subChannel);


                    // 写入WS身份信息
                    RedisQueueHelper.HashSet(RedisQueueHelperParameter.WebSocketByToken, token, client.TryToJson());
                }

                return new MethodResult<bool> { IsFinished = true, Date = true };
            }
            catch (Exception e)
            {
                return new MethodResult<bool> { IsFinished = false, Discription = e.Message };
            }
        }






        public MethodResult<bool> Fail(QueueRequest model)
        {
            if (model?.Tokens.Count > 0)
            {
                foreach (var token in model.Tokens)
                {
                    this.iLog.Queue<QueueRequest>(new
                    {
                        Client = CoreStartupHelper.GetConfigValue("Service:Title")
                    }.TryToJson(), "fail", model.projectToken, token);
                }

                return new MethodResult<bool> { IsFinished = true, Date = true };
            }
            else
                return new MethodResult<bool> { IsFinished = true, Date = false };
        }

        public MethodResult<bool> Confirm(QueueRequest model)
        {

            if (model?.Tokens.Count > 0)
            {
                foreach (var token in model.Tokens)
                {
                    this.iLog.Queue<QueueRequest>(new
                    {
                        Client = CoreStartupHelper.GetConfigValue("Service:Title")
                    }.TryToJson(), "confirm", model.projectToken, token);
                }

                return new MethodResult<bool> { IsFinished = true, Date = true };
            }
            else
                return new MethodResult<bool> { IsFinished = true, Date = false };
        }

        public MethodResult<bool> Finished(QueueRequest model)
        {
            if (model?.Tokens.Count > 0)
            {
                foreach (var token in model.Tokens)
                {
                    this.iLog.Queue<QueueRequest>(new
                    {
                        Client = CoreStartupHelper.GetConfigValue("Service:Title")
                    }.TryToJson(), "finished", model.projectToken, token);
                }

                return new MethodResult<bool> { IsFinished = true, Date = true };
            }
            else
                return new MethodResult<bool> { IsFinished = true, Date = false };
        }


        #region private


        /// <summary>
        /// 获取所有服务集合
        /// </summary>
        /// <param name="redis"></param>
        /// <returns></returns>
        private KeyValuePair<string,QueueService>[] GetQueueService(RedisHelper redis, QueryMessage model)
        {

            // 获取所有客户机
            var keys = redis.HashKeys(RedisQueueHelperParameter.QueueService);

            Dictionary<string, QueueService> dic
                = new Dictionary<string, QueueService>();

            // 构造服务列表
            foreach (var item in keys)
                dic.Add(item, redis.HashGet<QueueService>(RedisQueueHelperParameter.QueueService, item));

            Func<Listener_Template, bool> where = (a) =>
            (
                a.template.ToLower() == model.Template.ToLower()
                && a.Subscription != null
                && a.Subscription.Contains(model.MessageKey)
            );

            // 筛选出符合条件的客户端
            return dic.Where(a => a.Value.Temps.Where(where).FirstOrDefault() != null).ToArray();


        }

        /// <summary>
        /// 任务分发
        /// </summary>
        /// <param name="model"></param>
        /// <param name="logerInfo"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private bool TaskDistribution(QueryMessage model, RunningLogerInfoModel logerInfo, MessageQueue msg)
        {
            var redis = new RedisHelper();

            var clients = this.GetQueueService(redis, model);

            var msgStr = msg.TryToJson();


            if (clients != null && clients.Count() > 0)
            {
                logerInfo.IsPublish = true;

                if (model.MessageType == MessageTypeEnum.Single)
                {
                    // 随机分配一个 客户端 [需要优化]
                    var i = new Random().Next(0, clients.Count() - 1);

                    redis.Publish(clients[i].Key, msgStr);

                    logerInfo.clients.Add(clients[i].Key);
                }

                else if (model.MessageType == MessageTypeEnum.Group)
                    foreach (var item in clients)
                    {
                        redis.Publish(item.Key, msgStr);
                        logerInfo.clients.Add(item.Key);
                    }
            }
            else
            {
                logerInfo.IsPublish = true;

                if (model.MessageType == MessageTypeEnum.Single)
                    RedisQueueHelper
                        .ListPush(RedisQueueHelperParameter.SingleNull, msgStr);

                RedisQueueHelper.ListPush(
                    RedisQueueHelperParameter.History,
                    msgStr);

                iLog.Queue<Queue>(
                    "暂无可用服务端，加入离线消息",
                    "push_fail",
                    msg.ClientInfo.Project.ProjectToken,
                    model.Token);
            }

            iLog
                .Queue<Queue>(
                    logerInfo.TryToJson(), 
                    "request", 
                    msg.ClientInfo.Project.ProjectToken, 
                    model.Token);

            return true;
        }

        /// <summary>
        /// 构造发送数据
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private MessageQueue StructureMessage(QueryMessage model)
        {

            // 根据token 获取登陆信息
            var result
                = RedisQueueHelper.HashGet(RedisQueueHelperParameter.WebSocketByToken, model.ClientToken);

            // 判断是否登陆
            if (!string.IsNullOrWhiteSpace(result))
            {
                var client = ((string)result).TryToEntity<ClientInfo>();

                return new MessageQueue
                {
                    Template = model.Template.ToLower(),
                    ClientInfo = new ClientInfo
                    {
                        User = new UserInfo
                        {
                            Key = client.User.Key,
                            Parameter = client.User.Parameter
                        },
                        Project = new ProjectInfo
                        {
                            ProjectToken = client.Project.ProjectToken,
                            CallUrl = client.Project.CallUrl
                        }
                    },
                    Message = new Message
                    {
                        Token = model.Token,
                        Channel = model.MessageKey,
                        Content = model.Content,
                        MessageType = model.MessageType,
                        Parameter = model.Parameter,
                        SendDateTime = model.SendDateTime
                    }
                };
            }

            return null;
        }



        /// <summary>
        /// 构造测试发送数据
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private MessageQueue DebugStructureMessage(QueryMessageTest model)
        {

            // 判断是否登陆
            return new MessageQueue
            {
                Template = model.Template.ToLower(),
                ClientInfo = new ClientInfo
                {
                    User = new UserInfo
                    {
                        Key = "test",
                        Parameter = new
                        {
                            client = "ios",
                            headImg = "http://img5.duitang.com/uploads/item/201510/18/20151018121717_GVcJz.jpeg",
                            userName = "System"
                        }.TryToJson()
                    },
                    Project = new ProjectInfo
                    {
                        ProjectToken = model.ProjectToken,
                        CallUrl = model.CallUrl
                    }
                },
                Message = new Message
                {
                    Token = model.Token,
                    Channel = model.MessageKey,
                    Content = model.Content,
                    MessageType = model.MessageType,
                    Parameter = model.Parameter,
                    SendDateTime = model.SendDateTime
                }
            };

        }

        


        #endregion





    }
}
